//
// Created by liwen on 2024/3/26.
//

#include "httplib_server_session.h"
#include "rapidjson/Rjson.hpp"
#include "log_helper.h"
#include "httplib.h"
#include "General_exception2.h"

using namespace rapidjson;

void httplib_service_start(int port){
    {
        Document docArr;
        docArr.SetArray();

        Value obj = Rjson::rObject();
        obj.AddMember("uuid", 12, docArr.GetAllocator());
        obj.AddMember("port", port, docArr.GetAllocator());

        docArr.PushBack(obj, docArr.GetAllocator());

        rapidjson::StringBuffer strBuf;
        rapidjson::Writer<rapidjson::StringBuffer> writer(strBuf);
        docArr.Accept(writer);

        LOG_INFO(MAIN_LOG,"Rjson Test {}", strBuf.GetString());
    }

    httplib::Server svr;

    svr.set_logger([](const auto& req, const auto& res) {
        LOG_INFO(HTTP_LOG,"path:{} \nbody:{} \nres:{}",req.path, req.body, res.body);
    });

    svr.set_error_handler([](const auto& req, auto& res) {
        std::string resStr = "{\"code\":-1,\"message\":\"unhandled url\"}";
        res.set_content(resStr, "application/json");
    });

    svr.set_exception_handler([](const auto& req, auto& res, std::exception_ptr ep) {
        auto fmt = "{\"code\":-1,\"message\":\"Error 500\"}";
        char buf[BUFSIZ];
        try {
            std::rethrow_exception(ep);
        } catch (std::exception &e) {
            snprintf(buf, sizeof(buf), fmt, e.what());
        } catch (...) { // See the following NOTE
            snprintf(buf, sizeof(buf), fmt, "Unknown Exception");
        }
        res.set_content(buf, "application/json");
        res.status = httplib::StatusCode::InternalServerError_500;
    });

    svr.Get("/test",[&](const httplib::Request &req, httplib::Response &res) {
        if (!req.has_param("uid"))
        {
            LOG_ERROR(HTTP_LOG, "http request 'uid' field Not found! return 404");
            res.status = httplib::StatusCode::NotFound_404;
            return ;
        }
        std::string uid = req.get_param_value("uid",0);
        Document wdc = Rjson::rWriteDC();
        Value data = Rjson::rObject();
        for (auto & [key, value]:req.params) {
            Rjson::rAdd(wdc, data, key.c_str(), value);
        }
        Rjson::rAdd(wdc, "data", data);
        Rjson::rAdd(wdc, "cdoe", 0);
        Rjson::rAdd(wdc, "message", StringRef("success"));

        res.set_content(Rjson::ToString(wdc), "application/json");
    });

    svr.Post("/content_receiver", [&](const httplib::Request &req, httplib::Response &res, const httplib::ContentReader &content_reader) {
        if (req.is_multipart_form_data()) {
            // NOTE: `content_reader` is blocking until every form data field is read
            httplib::MultipartFormDataItems files;
            content_reader(
                    [&](const httplib::MultipartFormData &file) {
                        files.push_back(file);
                        return true;
                    },
                    [&](const char *data, size_t data_length) {
                        files.back().content.append(data, data_length);
                        return true;
                    });
            for (auto&item:files) {
                LOG_INFO(HTTP_LOG,"name:{} conent:{}", item.name, item.content);
            }
        } else {
            std::string body;
            content_reader([&](const char *data, size_t data_length) {
                body.append(data, data_length);
                return true;
            });
            LOG_INFO(HTTP_LOG,"body", body);
        }
        res.set_content("ok","text/plain");
    });

    const size_t DATA_CHUNK_SIZE = 4;
    svr.Get("/stream", [&](const httplib::Request &req, httplib::Response &res) {

        auto data = new std::string("ilong hello world!");

        res.set_content_provider(
                data->size(), // Content length
                "text/plain", // Content type
                [&, data](size_t offset, size_t length, httplib::DataSink &sink) {
                    const auto &d = *data;
                    sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
                    return true; // return 'false' if you want to cancel the process.
                },
                [data](bool success) { delete data; });
    });

    svr.set_mount_point("/", "./www");

    svr.listen("0.0.0.0", port);
}